/* Name: Wormhole
 * Author: Cameron Hoerig
 * Date: 5-8-12
 * Description: The Wormhole class is used to create a graph edge between planets. An image is drawn
 *  in order to give more visual appeal and to let the player know what edges exist.
 * Revisions: See Subversion logs at http://code.google.com/p/software-engineering-2012/source/list
 */

import java.awt.BasicStroke;
import java.awt.Font;
import java.awt.Graphics2D;
import java.util.Iterator;
import java.util.LinkedList;
import java.awt.Color;

public class Wormhole extends Item {
	private static LinkedList<Wormhole> idList = new LinkedList<Wormhole>();
	private Sprite validEnd;
	private Sprite validLine;
	private Sprite invalidEnd;
	private Sprite invalidLine;
	public static Boolean newWormhole = true;
	private Boolean placed = false;
	private Boolean validPlacement = true;
	private Planet startPlanet;
	private Item endItem;
	private int dx = 5;
	private int dy = 5;
	private int animPlaceX;
	private int animPlaceY;
	private int animDir = 1;
	public Boolean forceOff = false;
	public Boolean active = true;
	public float cost;
	public float unitFuelCost;
	private int disFromEarth;
	
	// Creates a wormhole starting at the given planet
	public Wormhole(Planet startingPlanet) {	
		Game.create((Item)this, 11); // Needed for update, draw, and destroy
		colType = COL_LINE; // Needed for collision checking
		idList.add(this); // Needed for collision checking
		startPlanet = startingPlanet;
		x = startPlanet.x;
		y = startPlanet.y;	
		x2 = ViewControl.mouseX();
		y2 = ViewControl.mouseY();
		animPlaceX = x;
		animPlaceY = y;		
		r = 5;
	}
	
	// Creates a wormhole going from the given planet to the given item
	public Wormhole(Planet startingPlanet, Item endingItem) {	
		Game.create((Item)this, 11); // Needed for update, draw, and destroy
		colType = COL_LINE; // Needed for collision checking
		idList.add(this); // Needed for collision checking
		startPlanet = startingPlanet;
		x = startPlanet.x;
		y = startPlanet.y;	
		x2 = ViewControl.mouseX();
		y2 = ViewControl.mouseY();	
		r = 5;
		
		placed = true;
		endItem = endingItem;
		x2 = endingItem.x;
		y2 = endingItem.y;
		animPlaceX = x2;
		animPlaceY = y2;
		startPlanet.addWormhole(this);
		if (endingItem instanceof Nebula) { // Handle ending on a nebula
			Nebula endNebula = (Nebula)endingItem;
			endNebula.addWormhole(this);
		} else if (endingItem instanceof AsteroidField) { // Handle ending on an asteroid field
			AsteroidField endAst = (AsteroidField)endingItem;
			endAst.addWormhole(this);
		}
	}
	
	// Handles updating info for the wormhole
	public void update() {
		validPlacement = true;
		if(!placed){ // Draw when user is still placing wormhole
			newWormhole = false;
			validPlacement = true;
			if (ViewControl.mouseX() != 999999) {
				this.x2 = ViewControl.mouseX();
				this.y2 = ViewControl.mouseY();
			}
			Item pid = Planet.getWormholeEnd();
			Item nid = Nebula.getWormholeEnd();
			Item aid = AsteroidField.getWormholeEnd();
			if(pid != null){
				Item tempItem = Planet.getWormholeEnd();
				if (!Fog.pointIsInside(tempItem.x, tempItem.y)) {
					this.x2 = tempItem.x;
					this.y2 = tempItem.y;			
				} else {
					validPlacement = false;
				}
			}
			else if(nid != null){ // Handle fog
				Item tempItem = nid;
				if (!Fog.pointIsInside(tempItem.x, tempItem.y)) {
					this.x2 = tempItem.x;
					this.y2 = tempItem.y;			
				} else {
					validPlacement = false;
				}
			}
			else if(aid != null){
				Item tempItem = aid;
				if (!Fog.pointIsInside(tempItem.x, tempItem.y)) {
					this.x2 = tempItem.x;
					this.y2 = tempItem.y;			
				} else {
					validPlacement = false;
				}
			}
			if (Fog.pointIsInside(x2, y2)) {
				validPlacement = false;
			}
			
		}else{ // Calculate cost
			newWormhole = true;
			float mag = (float)Math.sqrt(Math.pow(x2 - x, 2) + Math.pow(y2-y, 2));
			unitFuelCost = mag/(float)GameScreen.getWidth() * 3f;
		}
		
		active = true;
		if (forceOff) {
			active = false;
		} else { // Check for collisions
			Planet.checkCollision(this, true);
			Antimatter.checkCollision(this, true);
			AsteroidField.checkCollision(this, true);
			Nebula.checkCollision(this, true);		
			Blackhole.checkCollision(this, true);
			GammaRayBurst.checkCollision(this,true);
		}
		
		// Draw it!
		if(placed){
			final float angle = (float)Math.atan2((double)y2-(double)y,(double)x2-(double)x);
			int newMag = (int)Math.sqrt(Math.pow(animPlaceX - x, 2) + Math.pow(animPlaceY - y,2));
			if(newMag < 5){
				animPlaceX = x2;
				animPlaceY = y2;
				newMag = (int)Math.sqrt(Math.pow(animPlaceX - x, 2) + Math.pow(animPlaceY - y,2));
			}
			animPlaceX = x + (int)(((double)newMag - (double)5)*Math.cos(angle));
			animPlaceY = y + (int)(((double)newMag - (double)5)*Math.sin(angle));
			dx = x + (int)(((double)newMag + (double)5)*Math.cos(angle));
			dy = y + (int)(((double)newMag + (double)5)*Math.sin(angle));
		}
	}
	
	// Handle collisions with given objects
	public void collision(Item other) {
		if(other instanceof GammaRayBurst){
			active = false;
		} else if(other instanceof Blackhole){
			active = false;
		}
		else if(other instanceof Blackhole){
			active = false;
		}
	}
	
	// Attempts to place the given wormhole and returns whether or not it was actually placed
	public Boolean placeWormhole(){
		// Make sure it is a valid placement, and user has enough to pay for it
		if(!placed && validPlacement && cost < GameScreen.totalOre){
			Item pid = Planet.getWormholeEnd();
			Item nid = Nebula.getWormholeEnd();
			Item aid = AsteroidField.getWormholeEnd();
			if(pid != null && !wormholeExists(startPlanet, pid)){
				placed = true;
				endItem = pid;
				x2 = pid.x;
				y2 = pid.y;
				animPlaceX = x2;
				animPlaceY = y2;
				Planet endPlanet = (Planet) pid;
				endPlanet.conquer();
				startPlanet.addWormhole(this);
				endPlanet.addWormhole(this);
				Planet.calculateDisFromEarth();
				Sound.effect(Sound.EFFECT_CREATEWH);
			} else if(nid != null && !wormholeExists(startPlanet, nid)){
				placed = true;
				endItem = nid;
				x2 = nid.x;
				y2 = nid.y;
				animPlaceX = x2;
				animPlaceY = y2;
				Nebula endNebula = (Nebula)nid;
				startPlanet.addWormhole(this);
				endNebula.addWormhole(this);
				Sound.effect(Sound.EFFECT_CREATEWH);
			}
			else if(aid != null && !wormholeExists(startPlanet, aid)){
				placed = true;
				endItem = aid;
				x2 = aid.x;
				y2 = aid.y;
				animPlaceX = x2;
				animPlaceY = y2;
				AsteroidField endField = (AsteroidField)aid;
				startPlanet.addWormhole(this);
				endField.addWormhole(this);
				Sound.effect(Sound.EFFECT_CREATEWH);
			}
			if(endItem instanceof Planet){
				if(startPlanet.getDistanceFromEarth() < ((Planet)endItem).getDistanceFromEarth()){
					disFromEarth = startPlanet.getDistanceFromEarth();
				}
				else{
					disFromEarth = ((Planet)endItem).getDistanceFromEarth();
				}
			}
			else{
				disFromEarth = startPlanet.getDistanceFromEarth();
			}
			GameScreen.wormholePlaced();
			Planet.removeOre(cost);
			float mag = (float)Math.sqrt(Math.pow(x2 - x, 2) + Math.pow(y2-y, 2));
			unitFuelCost = mag/(float)GameScreen.getWidth() * 3f;
		}
		
		// Draw it!
		final float angle = (float)Math.atan2((double)y2-(double)y,(double)x2-(double)x);
		int newMag = (int)Math.sqrt(Math.pow(animPlaceX - x, 2) + Math.pow(animPlaceY - y,2));
		if(newMag < 5){
			animPlaceX = x2;
			animPlaceY = y2;
			newMag = (int)Math.sqrt(Math.pow(animPlaceX - x, 2) + Math.pow(animPlaceY - y,2));
		}
		animPlaceX = x + (int)(((double)newMag - (double)5)*Math.cos(angle));
		animPlaceY = y + (int)(((double)newMag - (double)5)*Math.sin(angle));
		dx = x + (int)(((double)newMag + (double)5)*Math.cos(angle));
		dy = y + (int)(((double)newMag + (double)5)*Math.sin(angle));
		
		return placed;
	}
	
	// Cancel creation of wormhole
	public void cancelWormhole(){
		if(!placed){
			newWormhole = true;
			Game.delete(this);
		}
	}
	
	// Check where mouse was pressed and handle correctly 
	public void leftMousePress(){
		if (!placed) {return;}
		Item mouseClick = new Item();
		mouseClick.colType = Item.COL_CIRCLE;
		mouseClick.r = 5;
		mouseClick.x = ViewControl.mouseX();
		mouseClick.y = ViewControl.mouseY();
		if (intersection(mouseClick, this)) {
			forceOff = false;
		}		
	}
	
	// Handles deletions of wormholes
	public void rightMousePress(){
		if (!placed) {return;}
		Item mouseClick = new Item();
		mouseClick.colType = Item.COL_CIRCLE;
		mouseClick.r = 5;
		mouseClick.x = ViewControl.mouseX();
		mouseClick.y = ViewControl.mouseY();
		if (intersection(mouseClick, this)) {
			if (forceOff) { // Handle double clicks to delete
				Planet.addOre(this.cost/3f);
				Game.delete(this);
				Sound.effect(Sound.EFFECT_DELETEWH);
			} else { // Handle first right click to power down
				Sound.effect(Sound.EFFECT_POWERDOWN);
				forceOff = true;
			}
			Planet.calculateDisFromEarth();
		}		
	}
	
	// Gets rid of the wormhole connections from connected objects
	public void destroy() {
		startPlanet.removeWormhole(this);
		if(endItem instanceof Planet){
			((Planet)endItem).removeWormhole(this);
		}
		else if(endItem instanceof Nebula){
			((Nebula)endItem).removeWormhole(this);
		}
		else if(endItem instanceof AsteroidField){
			((AsteroidField)endItem).removeWormhole(this);
		}
		idList.remove(this); // Needed for collision checking
		if (idList.size() == 0 && !Game.changingRooms) {
			GameScreen.loseGame();
		}
		deleteAllTemp();
	}
	
	// Deletes any wormholes currently being placed
	public static void deleteAllTemp() {
		// Delete any new wormholes
		for(Iterator it = idList.iterator(); it.hasNext();) {
	    	Wormhole wid = (Wormhole) it.next();
	    	if(!wid.placed){
	    		wid.cancelWormhole();
	    	}
		}
	}
	
	// Draws the wormhole
	public void draw() {
		Graphics2D gh = Game.getGraphicsHandle();
		gh.setStroke(new BasicStroke(5f));
		if(!placed){ // Draw wormhole being created
			if(validPlacement & active & cost < GameScreen.totalOre){
				gh.setColor(Color.BLUE);
				gh.drawLine(x, y, x2, y2);
			}
			else{
				gh.setColor(Color.RED);
				gh.drawLine(x, y, x2, y2);
			}
		} else { // Draw completed wormholes
			if(validPlacement & active){
				gh.setColor(Color.BLUE);
				gh.drawLine(x, y, animPlaceX, animPlaceY);
				gh.setColor(Color.ORANGE);
				gh.drawLine(animPlaceX, animPlaceY, dx, dy);
				gh.setColor(Color.BLUE);
				gh.drawLine(dx, dy, x2, y2);
			}
			else{ // Draw broken (collisions / cost) wormholes
				gh.setColor(Color.RED);
				gh.drawLine(x, y, animPlaceX, animPlaceY);
				gh.setColor(Color.ORANGE);
				gh.drawLine(animPlaceX, animPlaceY, dx, dy);
				gh.setColor(Color.RED);
				gh.drawLine(dx, dy, x2, y2);
			}
		}
	}
	
	// Draws the cost of the wormhole
	public static void drawCost() {
	    for(Iterator it = idList.iterator(); it.hasNext();) {
	    	Wormhole wid = (Wormhole) it.next();
	    	if(!wid.placed){ // Display wormhole cost
				Graphics2D gh = Game.getGraphicsHandle();
				float mag = (float)Math.sqrt(Math.pow(wid.x2 - wid.x, 2) + Math.pow(wid.y2-wid.y, 2));
				wid.cost = (float)mag/GameScreen.getHeight() * 7f * (float)Math.sqrt((float)idList.size());
				wid.unitFuelCost = mag/(float)GameScreen.getWidth()*3f;
				String oreString = String.format("Ore Cost: %.2f", wid.cost);
				String fuelString = String.format("Fuel Cost: %.2f/s", wid.unitFuelCost);
				gh.setFont(new Font("Serif", Font.PLAIN, 20));
				gh.setColor(Color.WHITE);
				gh.drawString(oreString, wid.x2 + 5, wid.y2 - 5);
				gh.drawString(fuelString, wid.x2+5, wid.y2+17);
	    	}
	    }
	}
	
	// Function needed in each item class for collision checking
	public static int checkCollision(Item other, Boolean notify) {
		int num = 0;
	    for(Iterator it = idList.iterator(); it.hasNext();) {
	    	Item idOn = (Item)it.next();
			if (idOn != other) {
				if (Item.intersection(other, idOn)) {
					if (notify) {
						idOn.collision(other);
					}
					if(other.x != idOn.x & other.x != idOn.x2 & other.y != idOn.y & other.y != idOn.y2 &
							other.x2 != idOn.x & other.x2 != idOn.x2 & other.y2 != idOn.y & other.y2 != idOn.y2){ 
						num++;
					}
				}
			}
		}
		return num;
	}
	
	public void setPlacementValidity(Boolean isValid){
		this.validPlacement = isValid;
	}
	
	// Returns Planet connected to given planet
	public Planet getOtherPlanet(Planet myId) {
		if (startPlanet != myId) {
			return startPlanet;
		} 
		else if (endItem != myId && endItem instanceof Planet) {
			Planet endPlanet = (Planet)endItem;
			return (Planet)endItem;
		} 
		else{
			return null;
		}
	}
	
	// Returns Planet connected to given item
	public Planet getOtherPlanet(Item myId) {
		if(myId instanceof Planet){
			return getOtherPlanet((Planet)myId);
		} 
		else {
			return startPlanet;
		}
	}
	
	// Sets wormhole animation
	private void setCloserToEarth(Planet pid) {
		if (startPlanet != pid) {
			endItem = (Item) startPlanet;
			startPlanet = pid;
			int tmpX = x;
			int tmpY = y;
			x = x2;
			y = y2;
			x2 = tmpX;
			y2 = tmpY;
		}		
	}
	
	// Determines the direction of the animation so it moves towards earth
	public static void determineDirections() {
		for(Iterator<Wormhole> it = idList.iterator(); it.hasNext();){
			Wormhole wid = (Wormhole)it.next();
			if (wid.endItem instanceof Planet) {
				Planet endP = (Planet) wid.endItem;
				if (wid.startPlanet.getDistanceFromEarth() > endP.getDistanceFromEarth()) {
					wid.endItem = (Item) wid.startPlanet;
					wid.startPlanet = endP;
					int tmpX = wid.x;
					int tmpY = wid.y;
					wid.x = wid.x2;
					wid.y = wid.y2;
					wid.x2 = tmpX;
					wid.y2 = tmpY;
				}
			}
		}
	}
	
	// Draws small icons for wormhole in top down view
	public static void drawTdIcons(float sf) {
		Graphics2D gh = Game.getGraphicsHandle();
		gh.setStroke(new BasicStroke(2f));
		for(Iterator it = idList.iterator(); it.hasNext();){ // Goes through all wormholes
			Wormhole wid = (Wormhole)it.next();
			if (wid.placed) { // Draws proper color
				if (wid.active) {
					gh.setColor(Color.BLUE);
				} else {
					gh.setColor(Color.RED);
				}
				gh.drawLine((int)(wid.x*sf), (int)(wid.y*sf), (int)(wid.x2*sf), (int)(wid.y2*sf));
			}
		}
	}
	
	// Returns whether or not a wormhole exists between the two given items
	private static boolean wormholeExists(Planet pid, Item iid) {
		for(Iterator it = idList.iterator(); it.hasNext();){
			Wormhole wid = (Wormhole)it.next();
			if (wid.startPlanet == pid && wid.endItem == iid) {
				return true;
			}
			if (wid.startPlanet == iid && wid.endItem == pid) {
				return true;
			}
		}
		return false;
	}
	
	// Returns the total wormhole distance for all wormholes connecting planets
	public static float getTotalDist() {
		float sum = 0.0f;
		// Only add length if connected to two planets
		for(Iterator it = idList.iterator(); it.hasNext();){
			Wormhole wid = (Wormhole)it.next();
			if (wid.placed && wid.endItem instanceof Planet) {
				sum += getDistance(wid.x, wid.y, wid.x2, wid.y2);
			}
		}
		return sum;
	}
	
	// Returns the distance between two points
	private static long getDistance(long x1, long y1, long x2, long y2) {
		return (long)Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));
	}
	
	// Pulses wormhole when not enough fuel for it before removing
	public static void secondPulse(){
		Boolean enoughFuel = false;
		float totalFuelRemoved = 0;
		while(!enoughFuel){
			totalFuelRemoved = 0;
			if(idList.size() == 0){
				break;
			}
			for(Iterator<Wormhole> it = idList.iterator(); it.hasNext();){
				Wormhole nextHole = (Wormhole)it.next();
				if(nextHole.active && nextHole.placed){
					totalFuelRemoved += nextHole.unitFuelCost;
				}
			}
			if(totalFuelRemoved <= GameScreen.totalFuel){
				Planet.removeFuel(totalFuelRemoved);
				enoughFuel = true;
			}
			else{
				removeFarthestWormhole();
				enoughFuel = false;
			}
		}
	}
	
	// Finds the furthest wormhole away from earth, and gets rid of it
	private static void removeFarthestWormhole(){
		Wormhole farthestHole = idList.get(0);
		for(Iterator<Wormhole> it = idList.iterator(); it.hasNext();){
			Wormhole nextHole = (Wormhole)it.next();
			if(nextHole.disFromEarth > farthestHole.disFromEarth && nextHole.active){
				farthestHole = nextHole;
			}
		}
		farthestHole.destroy();
		Sound.effect(Sound.EFFECT_DELETEWH);
		Game.delete(farthestHole);
	}
	
	// Returns a randomly selected wormhole from all in existence
	public static Wormhole getRandomWormhole() {
		if (idList.isEmpty()) {return null;}
		if (idList.getLast().placed) {
			return idList.get(Game.randomInt(idList.size()));
		} else {
			if (idList.size() > 1) {return null;}
			return idList.get(Game.randomInt(idList.size()-1));
		}
	}
}

